home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / mtx.c < prev    next >
C/C++ Source or Header  |  1996-10-15  |  59KB  |  2,030 lines

  1. /*
  2.  * Program:    MTX mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 May 1990
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. extern int errno;        /* just in case */
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <pwd.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include "mtx.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "dummy.h"
  50.  
  51. /* Mtx mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER mtxdriver = {
  57.   "mtx",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   mtx_valid,            /* mailbox is valid for us */
  60.   mtx_parameters,        /* manipulate parameters */
  61.   mtx_find,            /* find mailboxes */
  62.   mtx_find_bboards,        /* find bboards */
  63.   mtx_find_all,            /* find all mailboxes */
  64.   mtx_find_all_bboards,        /* find all bboards */
  65.   mtx_subscribe,        /* subscribe to mailbox */
  66.   mtx_unsubscribe,        /* unsubscribe from mailbox */
  67.   mtx_subscribe_bboard,        /* subscribe to bboard */
  68.   mtx_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   mtx_create,            /* create mailbox */
  70.   mtx_delete,            /* delete mailbox */
  71.   mtx_rename,            /* rename mailbox */
  72.   mtx_open,            /* open mailbox */
  73.   mtx_close,            /* close mailbox */
  74.   mtx_fetchfast,        /* fetch message "fast" attributes */
  75.   mtx_fetchflags,        /* fetch message flags */
  76.   mtx_fetchstructure,        /* fetch message envelopes */
  77.   mtx_fetchheader,        /* fetch message header only */
  78.   mtx_fetchtext,        /* fetch message body only */
  79.   mtx_fetchbody,        /* fetch message body section */
  80.   mtx_setflag,            /* set message flag */
  81.   mtx_clearflag,        /* clear message flag */
  82.   mtx_search,            /* search for message based on criteria */
  83.   mtx_ping,            /* ping mailbox to see if still alive */
  84.   mtx_check,            /* check for new messages */
  85.   mtx_expunge,            /* expunge deleted messages */
  86.   mtx_copy,            /* copy messages to another mailbox */
  87.   mtx_move,            /* move messages to another mailbox */
  88.   mtx_append,            /* append string message to mailbox */
  89.   mtx_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM mtxproto = {&mtxdriver};
  94.  
  95. /* Mtx mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *mtx_valid (name)
  101.     char *name;
  102. {
  103.   char tmp[MAILTMPLEN];
  104.   return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
  105. }
  106.  
  107.  
  108. /* Mtx mail test for valid mailbox
  109.  * Accepts: mailbox name
  110.  * Returns: T if valid, NIL otherwise
  111.  */
  112.  
  113. int mtx_isvalid (name,tmp)
  114.     char *name;
  115.     char *tmp;
  116. {
  117.   int fd;
  118.   int ret = NIL;
  119.   char *s,file[MAILTMPLEN];
  120.   struct stat sbuf;
  121.   time_t tp[2];
  122.   errno = EINVAL;        /* assume invalid argument */
  123.                 /* if file, get its status */
  124.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  125.       (s = mtx_file (file,name)) && !stat (s,&sbuf)) {
  126.     if (!sbuf.st_size) {    /* allow empty file if INBOX */
  127.       if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
  128.       else errno = 0;        /* empty file */
  129.     }
  130.     else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
  131.       memset (tmp,'\0',MAILTMPLEN);
  132.       if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
  133.       (s[1] == '\012')) {    /* valid format? */
  134.     *s = '\0';        /* tie off header */
  135.                 /* must begin with dd-mmm-yy" */
  136.     ret = (((tmp[2] == '-' && tmp[6] == '-') ||
  137.         (tmp[1] == '-' && tmp[5] == '-')) &&
  138.            (s = strchr (tmp+20,',')) && strchr (s+2,';')) ? T : NIL;
  139.       }
  140.       else errno = -1;        /* bogus format */
  141.       close (fd);        /* close the file */
  142.       tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  143.       tp[1] = sbuf.st_mtime;
  144.       utime (file,tp);        /* set the times */
  145.     }
  146.   }
  147.   return ret;            /* return what we should */
  148. }
  149.  
  150.  
  151. /* Mtx manipulate driver parameters
  152.  * Accepts: function code
  153.  *        function-dependent value
  154.  * Returns: function-dependent return value
  155.  */
  156.  
  157. void *mtx_parameters (function,value)
  158.     long function;
  159.     void *value;
  160. {
  161.   return NIL;
  162. }
  163.  
  164. /* Mtx mail find list of mailboxes
  165.  * Accepts: mail stream
  166.  *        pattern to search
  167.  */
  168.  
  169. void mtx_find (stream,pat)
  170.     MAILSTREAM *stream;
  171.     char *pat;
  172. {
  173.   if (stream) dummy_find (NIL,pat);
  174. }
  175.  
  176.  
  177. /* Mtx mail find list of bboards
  178.  * Accepts: mail stream
  179.  *        pattern to search
  180.  */
  181.  
  182. void mtx_find_bboards (stream,pat)
  183.     MAILSTREAM *stream;
  184.     char *pat;
  185. {
  186.   if (stream) dummy_find_bboards (NIL,pat);
  187. }
  188.  
  189.  
  190. /* Mtx mail find list of all mailboxes
  191.  * Accepts: mail stream
  192.  *        pattern to search
  193.  */
  194.  
  195. void mtx_find_all (stream,pat)
  196.     MAILSTREAM *stream;
  197.     char *pat;
  198. {
  199.   if (stream) dummy_find_all (NIL,pat);
  200. }
  201.  
  202.  
  203. /* Mtx mail find list of all bboards
  204.  * Accepts: mail stream
  205.  *        pattern to search
  206.  */
  207.  
  208. void mtx_find_all_bboards (stream,pat)
  209.     MAILSTREAM *stream;
  210.     char *pat;
  211. {
  212.   if (stream) dummy_find_all_bboards (NIL,pat);
  213. }
  214.  
  215. /* Mtx mail subscribe to mailbox
  216.  * Accepts: mail stream
  217.  *        mailbox to add to subscription list
  218.  * Returns: T on success, NIL on failure
  219.  */
  220.  
  221. long mtx_subscribe (stream,mailbox)
  222.     MAILSTREAM *stream;
  223.     char *mailbox;
  224. {
  225.   char tmp[MAILTMPLEN];
  226.   return sm_subscribe (mtx_file (tmp,mailbox));
  227. }
  228.  
  229.  
  230. /* Mtx mail unsubscribe to mailbox
  231.  * Accepts: mail stream
  232.  *        mailbox to delete from subscription list
  233.  * Returns: T on success, NIL on failure
  234.  */
  235.  
  236. long mtx_unsubscribe (stream,mailbox)
  237.     MAILSTREAM *stream;
  238.     char *mailbox;
  239. {
  240.   char tmp[MAILTMPLEN];
  241.   return sm_unsubscribe (mtx_file (tmp,mailbox));
  242. }
  243.  
  244.  
  245. /* Mtx mail subscribe to bboard
  246.  * Accepts: mail stream
  247.  *        bboard to add to subscription list
  248.  * Returns: T on success, NIL on failure
  249.  */
  250.  
  251. long mtx_subscribe_bboard (stream,mailbox)
  252.     MAILSTREAM *stream;
  253.     char *mailbox;
  254. {
  255.   return NIL;            /* never valid for Mtx */
  256. }
  257.  
  258.  
  259. /* Mtx mail unsubscribe to bboard
  260.  * Accepts: mail stream
  261.  *        bboard to delete from subscription list
  262.  * Returns: T on success, NIL on failure
  263.  */
  264.  
  265. long mtx_unsubscribe_bboard (stream,mailbox)
  266.     MAILSTREAM *stream;
  267.     char *mailbox;
  268. {
  269.   return NIL;            /* never valid for Mtx */
  270. }
  271.  
  272. /* Mtx mail create mailbox
  273.  * Accepts: MAIL stream
  274.  *        mailbox name to create
  275.  * Returns: T on success, NIL on failure
  276.  */
  277.  
  278. long mtx_create (stream,mailbox)
  279.     MAILSTREAM *stream;
  280.     char *mailbox;
  281. {
  282.   return dummy_create (stream,mailbox);
  283. }
  284.  
  285.  
  286. /* Mtx mail delete mailbox
  287.  * Accepts: MAIL stream
  288.  *        mailbox name to delete
  289.  * Returns: T on success, NIL on failure
  290.  */
  291.  
  292. long mtx_delete (stream,mailbox)
  293.     MAILSTREAM *stream;
  294.     char *mailbox;
  295. {
  296.   return mtx_rename (stream,mailbox,NIL);
  297. }
  298.  
  299. /* Mtx mail rename mailbox
  300.  * Accepts: MAIL stream
  301.  *        old mailbox name
  302.  *        new mailbox name (or NIL for delete)
  303.  * Returns: T on success, NIL on failure
  304.  */
  305.  
  306. long mtx_rename (stream,old,new)
  307.     MAILSTREAM *stream;
  308.     char *old;
  309.     char *new;
  310. {
  311.   long ret = T;
  312.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  313.   int ld;
  314.   int fd = open (mtx_file (file,old),O_RDWR,NIL);
  315.                 /* lock out non c-client applications */
  316.   if (fd < 0) {            /* open mailbox */
  317.     sprintf (tmp,"Can't open mailbox %s: %s",old,strerror (errno));
  318.     mm_log (tmp,ERROR);
  319.     return NIL;
  320.   }
  321.                 /* get exclusive parse/append permission */
  322.   if ((ld = mtx_lock (fd,lock,LOCK_EX)) < 0) {
  323.     mm_log ("Unable to lock rename mailbox",ERROR);
  324.     return NIL;
  325.   }
  326.                 /* lock out other users */
  327.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  328.     close (fd);            /* couldn't lock, give up on it then */
  329.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  330.     mm_log (tmp,ERROR);
  331.     mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  332.     return NIL;
  333.   }
  334.                 /* do the rename or delete operation */
  335.   if (new ? rename (file,mtx_file (tmp,new)) : unlink (file)) {
  336.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  337.          strerror (errno));
  338.     mm_log (tmp,ERROR);
  339.     ret = NIL;            /* set failure */
  340.   }
  341.   flock (fd,LOCK_UN);        /* release lock on the file */
  342.   mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  343.   close (fd);            /* close the file */
  344.   return ret;            /* return success */
  345. }
  346.  
  347. /* Mtx mail open
  348.  * Accepts: stream to open
  349.  * Returns: stream on success, NIL on failure
  350.  */
  351.  
  352. MAILSTREAM *mtx_open (stream)
  353.     MAILSTREAM *stream;
  354. {
  355.   int fd,ld;
  356.   char tmp[MAILTMPLEN];
  357.                 /* return prototype for OP_PROTOTYPE call */
  358.   if (!stream) return user_flags (&mtxproto);
  359.   if (LOCAL) {            /* close old file if stream being recycled */
  360.     mtx_close (stream);    /* dump and save the changes */
  361.     stream->dtb = &mtxdriver;    /* reattach this driver */
  362.     mail_free_cache (stream);    /* clean up cache */
  363.   }
  364.   user_flags (stream);        /* set up user flags */
  365.                 /* force readonly if bboard */
  366.   if (*stream->mailbox == '*') stream->rdonly = T;
  367.   if (stream->rdonly ||
  368.       (fd = open (mtx_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  369.     if ((fd = open (mtx_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  370.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  371.       mm_log (tmp,ERROR);
  372.       return NIL;
  373.     }
  374.     else if (!stream->rdonly) {    /* got it, but readonly */
  375.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  376.       stream->rdonly = T;
  377.     }
  378.   }
  379.   stream->local = fs_get (sizeof (MTXLOCAL));
  380.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  381.   LOCAL->buflen = MAXMESSAGESIZE;
  382.                 /* note if an INBOX or not */
  383.   LOCAL->inbox = !strcmp (ucase (strcpy (LOCAL->buf,stream->mailbox)),"INBOX");
  384.   if (*stream->mailbox != '*') {/* canonicalize the stream mailbox name */
  385.     fs_give ((void **) &stream->mailbox);
  386.     stream->mailbox = cpystr (tmp);
  387.   }
  388.                 /* get shared parse permission */
  389.   if ((ld = mtx_lock (fd,tmp,LOCK_SH)) < 0) {
  390.     mm_log ("Unable to lock open mailbox",ERROR);
  391.     return NIL;
  392.   }
  393.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  394.   mtx_unlock (ld,tmp);        /* release shared parse permission */
  395.   LOCAL->filesize = 0;        /* initialize parsed file size */
  396.   LOCAL->filetime = 0;        /* time not set up yet */
  397.   LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  398.   stream->sequence++;        /* bump sequence number */
  399.                 /* parse mailbox */
  400.   stream->nmsgs = stream->recent = 0;
  401.   if (mtx_ping (stream) && !stream->nmsgs)
  402.     mm_log ("Mailbox is empty",(long) NIL);
  403.   if (!LOCAL) return NIL;    /* failure if stream died */
  404.   return stream;        /* return stream to caller */
  405. }
  406.  
  407. /* Mtx mail close
  408.  * Accepts: MAIL stream
  409.  */
  410.  
  411. void mtx_close (stream)
  412.     MAILSTREAM *stream;
  413. {
  414.   if (stream && LOCAL) {    /* only if a file is open */
  415.     flock (LOCAL->fd,LOCK_UN);    /* unlock local file */
  416.     close (LOCAL->fd);        /* close the local file */
  417.                 /* free local text buffer */
  418.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  419.                 /* nuke the local data */
  420.     fs_give ((void **) &stream->local);
  421.     stream->dtb = NIL;        /* log out the DTB */
  422.   }
  423. }
  424.  
  425.  
  426. /* Mtx mail fetch fast information
  427.  * Accepts: MAIL stream
  428.  *        sequence
  429.  */
  430.  
  431. void mtx_fetchfast (stream,sequence)
  432.     MAILSTREAM *stream;
  433.     char *sequence;
  434. {
  435.   return;            /* no-op for local mail */
  436. }
  437.  
  438.  
  439. /* Mtx mail fetch flags
  440.  * Accepts: MAIL stream
  441.  *        sequence
  442.  */
  443.  
  444. void mtx_fetchflags (stream,sequence)
  445.     MAILSTREAM *stream;
  446.     char *sequence;
  447. {
  448.   long i;
  449.                 /* ping mailbox, get new status for messages */
  450.   if (mtx_ping (stream) && mail_sequence (stream,sequence))
  451.     for (i = 1; i <= stream->nmsgs; i++)
  452.       if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
  453. }
  454.  
  455. /* Mtx mail fetch structure
  456.  * Accepts: MAIL stream
  457.  *        message # to fetch
  458.  *        pointer to return body
  459.  * Returns: envelope of this message, body returned in body value
  460.  *
  461.  * Fetches the "fast" information as well
  462.  */
  463.  
  464. ENVELOPE *mtx_fetchstructure (stream,msgno,body)
  465.     MAILSTREAM *stream;
  466.     long msgno;
  467.     BODY **body;
  468. {
  469.   LONGCACHE *lelt;
  470.   ENVELOPE **env;
  471.   BODY **b;
  472.   STRING bs;
  473.   char *hdr,*text;
  474.   unsigned long hdrsize;
  475.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  476.   unsigned long textsize = body ? mtx_size (stream,msgno) - hdrsize : 0;
  477.   unsigned long i = max (hdrsize,textsize);
  478.   if (stream->scache) {        /* short cache */
  479.     if (msgno != stream->msgno){/* flush old poop if a different message */
  480.       mail_free_envelope (&stream->env);
  481.       mail_free_body (&stream->body);
  482.     }
  483.     stream->msgno = msgno;
  484.     env = &stream->env;        /* get pointers to envelope and body */
  485.     b = &stream->body;
  486.   }
  487.   else {            /* long cache */
  488.     lelt = mail_lelt (stream,msgno);
  489.     env = &lelt->env;        /* get pointers to envelope and body */
  490.     b = &lelt->body;
  491.   }
  492.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  493.     mail_free_envelope (env);    /* flush old envelope and body */
  494.     mail_free_body (b);
  495.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  496.       fs_give ((void **) &LOCAL->buf);
  497.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  498.     }
  499.                 /* get to header position */
  500.     lseek (LOCAL->fd,hdrpos,L_SET);
  501.                 /* read the header and text */
  502.     read (LOCAL->fd,hdr = (char *) fs_get (hdrsize + 1),hdrsize);
  503.     read (LOCAL->fd,text = (char *) fs_get (textsize + 1),textsize);
  504.     hdr[hdrsize] = '\0';    /* make sure tied off */
  505.     text[textsize] = '\0';    /* make sure tied off */
  506.     INIT (&bs,mail_string,(void *) text,textsize);
  507.                 /* parse envelope and body */
  508.     rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,mylocalhost (),
  509.               LOCAL->buf);
  510.     fs_give ((void **) &text);
  511.     fs_give ((void **) &hdr);
  512.   }
  513.   if (body) *body = *b;        /* return the body */
  514.   return *env;            /* return the envelope */
  515. }
  516.  
  517. /* Mtx mail fetch message header
  518.  * Accepts: MAIL stream
  519.  *        message # to fetch
  520.  * Returns: message header in RFC822 format
  521.  */
  522.  
  523. char *mtx_fetchheader (stream,msgno)
  524.     MAILSTREAM *stream;
  525.     long msgno;
  526. {
  527.   unsigned long hdrsize;
  528.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  529.   if (LOCAL->buflen < hdrsize) {/* fs_resize would do an unnecessary copy */
  530.     fs_give ((void **) &LOCAL->buf);
  531.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = hdrsize) + 1);
  532.   }
  533.   LOCAL->buf[hdrsize] = '\0';    /* tie off string */
  534.                 /* get to header position */
  535.   lseek (LOCAL->fd,hdrpos,L_SET);
  536.                 /* slurp the data */
  537.   read (LOCAL->fd,LOCAL->buf,hdrsize);
  538.   return LOCAL->buf;
  539. }
  540.  
  541.  
  542. /* Mtx mail fetch message text (only)
  543.     body only;
  544.  * Accepts: MAIL stream
  545.  *        message # to fetch
  546.  * Returns: message text in RFC822 format
  547.  */
  548.  
  549. char *mtx_fetchtext (stream,msgno)
  550.     MAILSTREAM *stream;
  551.     long msgno;
  552. {
  553.   unsigned long hdrsize;
  554.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  555.   unsigned long textsize = mtx_size (stream,msgno) - hdrsize;
  556.   if (LOCAL->buflen<textsize) {    /* fs_resize would do an unnecessary copy */
  557.     fs_give ((void **) &LOCAL->buf);
  558.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = textsize) + 1);
  559.   }
  560.   LOCAL->buf[textsize] = '\0';    /* tie off string */
  561.                 /* mark message as seen */
  562.   mtx_elt (stream,msgno)->seen = T;
  563.                 /* recalculate status */
  564.   mtx_update_status (stream,msgno,T);
  565.                 /* get to text position */
  566.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  567.                 /* slurp the data */
  568.   read (LOCAL->fd,LOCAL->buf,textsize);
  569.   return LOCAL->buf;
  570. }
  571.  
  572. /* Mtx fetch message body as a structure
  573.  * Accepts: Mail stream
  574.  *        message # to fetch
  575.  *        section specifier
  576.  *        pointer to length
  577.  * Returns: pointer to section of message body
  578.  */
  579.  
  580. char *mtx_fetchbody (stream,m,s,len)
  581.     MAILSTREAM *stream;
  582.     long m;
  583.     char *s;
  584.     unsigned long *len;
  585. {
  586.   BODY *b;
  587.   PART *pt;
  588.   char *t;
  589.   unsigned long i;
  590.   unsigned long base;
  591.   unsigned long offset = 0;
  592.   unsigned long hdrpos = mtx_header (stream,m,&base);
  593.   MESSAGECACHE *elt = mtx_elt (stream,m);
  594.                 /* make sure have a body */
  595.   if (!(mtx_fetchstructure (stream,m,&b) && b && s && *s &&
  596.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  597.   do {                /* until find desired body part */
  598.                 /* multipart content? */
  599.     if (b->type == TYPEMULTIPART) {
  600.       pt = b->contents.part;    /* yes, find desired part */
  601.       while (--i && (pt = pt->next));
  602.       if (!pt) return NIL;    /* bad specifier */
  603.                 /* note new body, check valid nesting */
  604.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  605.       offset = pt->offset;    /* get new offset */
  606.     }
  607.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  608.                 /* need to go down further? */
  609.     if (i = *s) switch (b->type) {
  610.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  611.       offset = b->contents.msg.offset;
  612.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  613.     case TYPEMULTIPART:        /* multipart, get next section */
  614.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  615.     default:            /* bogus subpart specification */
  616.       return NIL;
  617.     }
  618.   } while (i);
  619.                 /* lose if body bogus */
  620.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  621.   elt->seen = T;        /* mark message as seen */
  622.                 /* recalculate status */
  623.   mtx_update_status (stream,m,T);
  624.                 /* move to that place in the data */
  625.   lseek (LOCAL->fd,hdrpos + base + offset,L_SET);
  626.                 /* fs_resize would do an unnecessary copy */
  627.   if (LOCAL->buflen < b->size.ibytes) {
  628.     fs_give ((void **) &LOCAL->buf);
  629.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = b->size.ibytes) + 1);
  630.   }
  631.                 /* tie off string */
  632.   LOCAL->buf[b->size.ibytes] = '\0';
  633.   read (LOCAL->fd,LOCAL->buf,b->size.ibytes);
  634.   *len = b->size.ibytes;    /* return length */
  635.   return LOCAL->buf;
  636. }
  637.  
  638. /* Mtx locate header for a message
  639.  * Accepts: MAIL stream
  640.  *        message number
  641.  *        pointer to returned header size
  642.  * Returns: position of header in file
  643.  */
  644.  
  645. unsigned long mtx_header (stream,msgno,size)
  646.     MAILSTREAM *stream;
  647.     long msgno;
  648.     unsigned long *size;
  649. {
  650.   long siz;
  651.   long i = 0;
  652.   int q = 0;
  653.   char *s,tmp[MAILTMPLEN];
  654.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  655.   long pos = elt->data1 + (elt->data2 >> 24);
  656.                 /* is size known? */
  657.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  658.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  659.                 /* search message for CRLF CRLF */
  660.     for (siz = 0; siz < elt->rfc822_size; siz++) {
  661.                 /* read another buffer as necessary */
  662.       if (--i <= 0)        /* buffer empty? */
  663.     if (read (LOCAL->fd,s = tmp,
  664.            i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)
  665.       return pos;        /* I/O error? */
  666.       switch (q) {        /* sniff at buffer */
  667.       case 0:            /* first character */
  668.     q = (*s++ == '\015') ? 1 : 0;
  669.     break;
  670.       case 1:            /* second character */
  671.     q = (*s++ == '\012') ? 2 : 0;
  672.     break;
  673.       case 2:            /* third character */
  674.     q = (*s++ == '\015') ? 3 : 0;
  675.     break;
  676.       case 3:            /* fourth character */
  677.     if (*s++ == '\012') {    /* have the sequence? */
  678.                 /* yes, note for later */
  679.       elt->data2 |= (*size = siz);
  680.       return pos;        /* return to caller */
  681.     }
  682.     q = 0;            /* lost... */
  683.     break;
  684.       }
  685.     }
  686.   }
  687.   return pos;            /* have position */
  688. }
  689.  
  690. /* Mtx mail set flag
  691.  * Accepts: MAIL stream
  692.  *        sequence
  693.  *        flag(s)
  694.  */
  695.  
  696. void mtx_setflag (stream,sequence,flag)
  697.     MAILSTREAM *stream;
  698.     char *sequence;
  699.     char *flag;
  700. {
  701.   MESSAGECACHE *elt;
  702.   long i;
  703.   unsigned long uf;
  704.   short f;
  705.   struct stat sbuf;
  706.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  707.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  708.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  709.   }
  710.                 /* no-op if no flags to modify */
  711.   if (!((f = mtx_getflags (stream,flag,&uf)) || uf)) return;
  712.                 /* get sequence and loop on it */
  713.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  714.     if ((elt = mail_elt (stream,i))->sequence) {
  715.       mtx_elt (stream,i);    /* set all requested flags */
  716.       if (f&fSEEN) elt->seen = T;
  717.       if (f&fDELETED) elt->deleted = T;
  718.       if (f&fFLAGGED) elt->flagged = T;
  719.       if (f&fANSWERED) elt->answered = T;
  720.       elt->user_flags |= uf;
  721.                 /* recalculate status */
  722.       mtx_update_status (stream,i,NIL);
  723.     }
  724.   if (!stream->rdonly) {    /* make sure the update takes */
  725.     fsync (LOCAL->fd);
  726.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  727.     LOCAL->filetime = sbuf.st_mtime;
  728.   }
  729. }
  730.  
  731. /* Mtx mail clear flag
  732.  * Accepts: MAIL stream
  733.  *        sequence
  734.  *        flag(s)
  735.  */
  736.  
  737. void mtx_clearflag (stream,sequence,flag)
  738.     MAILSTREAM *stream;
  739.     char *sequence;
  740.     char *flag;
  741. {
  742.   MESSAGECACHE *elt;
  743.   long i;
  744.   unsigned long uf;
  745.   short f;
  746.   struct stat sbuf;
  747.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  748.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  749.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  750.   }
  751.                 /* no-op if no flags to modify */
  752.   if (!((f = mtx_getflags (stream,flag,&uf)) || uf)) return;
  753.                 /* get sequence and loop on it */
  754.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  755.     if ((elt = mail_elt (stream,i))->sequence) {
  756.       mtx_elt (stream,i);    /* clear all requested flags */
  757.       if (f&fSEEN) elt->seen = NIL;
  758.       if (f&fDELETED) elt->deleted = NIL;
  759.       if (f&fFLAGGED) elt->flagged = NIL;
  760.       if (f&fANSWERED) elt->answered = NIL;
  761.       elt->user_flags &= ~uf;
  762.                 /* recalculate status */
  763.       mtx_update_status (stream,i,NIL);
  764.     }
  765.   if (!stream->rdonly) {    /* make sure the update takes */
  766.     fsync (LOCAL->fd);
  767.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  768.     LOCAL->filetime = sbuf.st_mtime;
  769.   }
  770. }
  771.  
  772. /* Mtx mail search for messages
  773.  * Accepts: MAIL stream
  774.  *        search criteria
  775.  */
  776.  
  777. void mtx_search (stream,criteria)
  778.     MAILSTREAM *stream;
  779.     char *criteria;
  780. {
  781.   long i,n;
  782.   char *d;
  783.   search_t f;
  784.                 /* initially all searched */
  785.   for (i = 1; i <= stream->nmsgs; ++i) mtx_elt (stream,i)->searched = T;
  786.                 /* get first criterion */
  787.   if (criteria && (criteria = strtok (criteria," "))) {
  788.                 /* for each criterion */
  789.     for (; criteria; (criteria = strtok (NIL," "))) {
  790.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  791.       switch (*ucase (criteria)) {
  792.       case 'A':            /* possible ALL, ANSWERED */
  793.     if (!strcmp (criteria+1,"LL")) f = mtx_search_all;
  794.     else if (!strcmp (criteria+1,"NSWERED")) f = mtx_search_answered;
  795.     break;
  796.       case 'B':            /* possible BCC, BEFORE, BODY */
  797.     if (!strcmp (criteria+1,"CC"))
  798.       f = mtx_search_string (mtx_search_bcc,&d,&n);
  799.     else if (!strcmp (criteria+1,"EFORE"))
  800.       f = mtx_search_date (mtx_search_before,&n);
  801.     else if (!strcmp (criteria+1,"ODY"))
  802.       f = mtx_search_string (mtx_search_body,&d,&n);
  803.     break;
  804.       case 'C':            /* possible CC */
  805.     if (!strcmp (criteria+1,"C"))
  806.       f = mtx_search_string (mtx_search_cc,&d,&n);
  807.     break;
  808.       case 'D':            /* possible DELETED */
  809.     if (!strcmp (criteria+1,"ELETED")) f = mtx_search_deleted;
  810.     break;
  811.       case 'F':            /* possible FLAGGED, FROM */
  812.     if (!strcmp (criteria+1,"LAGGED")) f = mtx_search_flagged;
  813.     else if (!strcmp (criteria+1,"ROM"))
  814.       f = mtx_search_string (mtx_search_from,&d,&n);
  815.     break;
  816.       case 'K':            /* possible KEYWORD */
  817.     if (!strcmp (criteria+1,"EYWORD"))
  818.       f = mtx_search_flag (mtx_search_keyword,&n,stream);
  819.     break;
  820.       case 'N':            /* possible NEW */
  821.     if (!strcmp (criteria+1,"EW")) f = mtx_search_new;
  822.     break;
  823.  
  824.       case 'O':            /* possible OLD, ON */
  825.     if (!strcmp (criteria+1,"LD")) f = mtx_search_old;
  826.     else if (!strcmp (criteria+1,"N"))
  827.       f = mtx_search_date (mtx_search_on,&n);
  828.     break;
  829.       case 'R':            /* possible RECENT */
  830.     if (!strcmp (criteria+1,"ECENT")) f = mtx_search_recent;
  831.     break;
  832.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  833.     if (!strcmp (criteria+1,"EEN")) f = mtx_search_seen;
  834.     else if (!strcmp (criteria+1,"INCE"))
  835.       f = mtx_search_date (mtx_search_since,&n);
  836.     else if (!strcmp (criteria+1,"UBJECT"))
  837.       f = mtx_search_string (mtx_search_subject,&d,&n);
  838.     break;
  839.       case 'T':            /* possible TEXT, TO */
  840.     if (!strcmp (criteria+1,"EXT"))
  841.       f = mtx_search_string (mtx_search_text,&d,&n);
  842.     else if (!strcmp (criteria+1,"O"))
  843.       f = mtx_search_string (mtx_search_to,&d,&n);
  844.     break;
  845.       case 'U':            /* possible UN* */
  846.     if (criteria[1] == 'N') {
  847.       if (!strcmp (criteria+2,"ANSWERED")) f = mtx_search_unanswered;
  848.       else if (!strcmp (criteria+2,"DELETED")) f = mtx_search_undeleted;
  849.       else if (!strcmp (criteria+2,"FLAGGED")) f = mtx_search_unflagged;
  850.       else if (!strcmp (criteria+2,"KEYWORD"))
  851.         f = mtx_search_flag (mtx_search_unkeyword,&n,stream);
  852.       else if (!strcmp (criteria+2,"SEEN")) f = mtx_search_unseen;
  853.     }
  854.     break;
  855.       default:            /* we will barf below */
  856.     break;
  857.       }
  858.       if (!f) {            /* if can't determine any criteria */
  859.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  860.     mm_log (LOCAL->buf,ERROR);
  861.     return;
  862.       }
  863.                 /* run the search criterion */
  864.       for (i = 1; i <= stream->nmsgs; ++i)
  865.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  866.       mail_elt (stream,i)->searched = NIL;
  867.     }
  868.                 /* report search results to main program */
  869.     for (i = 1; i <= stream->nmsgs; ++i)
  870.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  871.   }
  872. }
  873.  
  874. /* Mtx mail ping mailbox
  875.  * Accepts: MAIL stream
  876.  * Returns: T if stream still alive, NIL if not
  877.  */
  878.  
  879. long mtx_ping (stream)
  880.     MAILSTREAM *stream;
  881. {
  882.   long i = 1;
  883.   long r = NIL;
  884.   int ld;
  885.   char lock[MAILTMPLEN];
  886.   struct stat sbuf;
  887.   if (stream && LOCAL) {    /* only if stream already open */
  888.     if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck)) {
  889.       fstat (LOCAL->fd,&sbuf);    /* get current write time */
  890.       if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  891.     }
  892.                 /* check for changed message status */
  893.     if (LOCAL->mustcheck || LOCAL->shouldcheck) {
  894.       if (LOCAL->shouldcheck)    /* babble when we do this unilaterally */
  895.     mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
  896.       while (i <= stream->nmsgs) mtx_elt (stream,i++);
  897.       LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  898.     }
  899.                 /* get shared parse/append permission */
  900.     if ((ld = mtx_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  901.                 /* parse resulting mailbox */
  902.       r = (mtx_parse (stream)) ? T : NIL;
  903.       mtx_unlock (ld,lock);    /* release shared parse/append permission */
  904.     }
  905.                 /* snarf if this is a read-write inbox */
  906.     if (stream && LOCAL && LOCAL->inbox && !stream->rdonly) {
  907.       mtx_snarf (stream);
  908.                 /* get shared parse/append permission */
  909.       if ((ld = mtx_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  910.                 /* parse resulting mailbox */
  911.     r = (mtx_parse (stream)) ? T : NIL;
  912.     mtx_unlock (ld,lock);    /* release shared parse/append permission */
  913.       }
  914.     }
  915.   }
  916.   return r;            /* return result of the parse */
  917. }
  918.  
  919.  
  920. /* Mtx mail check mailbox (too)
  921.     reparses status too;
  922.  * Accepts: MAIL stream
  923.  */
  924.  
  925. void mtx_check (stream)
  926.     MAILSTREAM *stream;
  927. {
  928.                 /* mark that a check is desired */
  929.   if (LOCAL) LOCAL->mustcheck = T;
  930.   if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
  931. }
  932.  
  933. /* Mtx mail snarf messages from system inbox
  934.  * Accepts: MAIL stream
  935.  */
  936.  
  937. void mtx_snarf (stream)
  938.     MAILSTREAM *stream;
  939. {
  940.   long i = 0;
  941.   long r,j;
  942.   struct stat sbuf;
  943.   struct iovec iov[3];
  944.   char lock[MAILTMPLEN];
  945.   MESSAGECACHE *elt;
  946.   MAILSTREAM *sysibx = NIL;
  947.   int ld;
  948.                 /* give up if can't get exclusive permission */
  949.   if ((!strcmp (sysinbox (),stream->mailbox)) ||
  950.       ((ld = mtx_lock (LOCAL->fd,lock,LOCK_EX)) < 0)) return;
  951.   mm_critical (stream);        /* go critical */
  952.   stat (sysinbox (),&sbuf);    /* see if anything there */
  953.   if (sbuf.st_size) {        /* non-empty? */
  954.     fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  955.                 /* sizes match and can get sysibx mailbox? */
  956.     if ((sbuf.st_size == LOCAL->filesize) &&
  957.     (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
  958.     (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
  959.                 /* yes, go to end of file in our mailbox */
  960.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  961.                 /* for each message in sysibx mailbox */
  962.       while (r && (++i <= sysibx->nmsgs)) {
  963.                 /* snarf message from Berkeley mailbox */
  964.     iov[1].iov_base = cpystr (mail_fetchheader (sysibx,i));
  965.     iov[1].iov_len = strlen (iov[1].iov_base);
  966.     iov[2].iov_base = mail_fetchtext (sysibx,i);
  967.     iov[2].iov_len = strlen (iov[2].iov_base);
  968.                 /* calculate header line */
  969.     mail_date ((iov[0].iov_base = LOCAL->buf),elt = mail_elt (sysibx,i));
  970.     sprintf(LOCAL->buf + strlen (LOCAL->buf),",%ld;0000000000%02o\015\012",
  971.         iov[1].iov_len + iov[2].iov_len,(fOLD * !(elt->recent)) +
  972.         (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  973.         (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  974.     iov[0].iov_len = strlen (iov[0].iov_base);
  975.                 /* copy message to new mailbox */
  976.     if ((writev (LOCAL->fd,iov,3) < 0) || fsync (LOCAL->fd)) {
  977.       sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  978.       mm_log (LOCAL->buf,ERROR);
  979.       ftruncate (LOCAL->fd,sbuf.st_size);
  980.       r = 0;        /* flag that we have lost big */
  981.     }
  982.     fs_give ((void **) &iov[1].iov_base);
  983.       }
  984.       fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  985.       LOCAL->filetime = sbuf.st_mtime;
  986.       if (r) {            /* delete all the messages we copied */
  987.     for (i = 1; i <= r; i++) mail_elt (sysibx,i)->deleted = T;
  988.     mail_expunge (sysibx);    /* now expunge all those messages */
  989.       }
  990.  
  991.     }
  992.     if (sysibx) mail_close (sysibx);
  993.   }
  994.   mm_nocritical (stream);    /* release critical */
  995.   mtx_unlock (ld,lock);        /* release exclusive parse/append permission */
  996. }
  997.  
  998. /* Mtx mail expunge mailbox
  999.  * Accepts: MAIL stream
  1000.  */
  1001.  
  1002. void mtx_expunge (stream)
  1003.     MAILSTREAM *stream;
  1004. {
  1005.   struct stat sbuf;
  1006.   off_t pos = 0;
  1007.   int ld;
  1008.   unsigned long i = 1;
  1009.   unsigned long j,k,m,recent;
  1010.   unsigned long n = 0;
  1011.   unsigned long delta = 0;
  1012.   char lock[MAILTMPLEN];
  1013.   MESSAGECACHE *elt;
  1014.                 /* do nothing if stream dead */
  1015.   if (!mtx_ping (stream)) return;
  1016.   if (stream->rdonly) {        /* won't do on readonly files! */
  1017.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  1018.     return;
  1019.   }
  1020.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  1021.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  1022.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  1023.   }
  1024.   /* The cretins who designed flock() created a window of vulnerability in
  1025.    * upgrading locks from shared to exclusive or downgrading from exclusive
  1026.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  1027.    * flock() actually *releases* the former lock.  Obviously they never talked
  1028.    * to any database guys.  Fortunately, we have the parse/append permission
  1029.    * lock.  If we require this lock before going exclusive on the mailbox,
  1030.    * another process can not sneak in and steal the exclusive mailbox lock on
  1031.    * us, because it will block on trying to get parse/append permission first.
  1032.    */
  1033.                 /* get exclusive parse/append permission */
  1034.   if ((ld = mtx_lock (LOCAL->fd,lock,LOCK_EX)) < 0) {
  1035.     mm_log ("Unable to lock expunge mailbox",ERROR);
  1036.     return;
  1037.   }
  1038.                 /* get exclusive access */
  1039.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  1040.     flock (LOCAL->fd,LOCK_SH);    /* recover previous lock */
  1041.     mm_log("Can't expunge because mailbox is in use by another process",ERROR);
  1042.     mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  1043.     return;
  1044.   }
  1045.  
  1046.   mm_critical (stream);        /* go critical */
  1047.   recent = stream->recent;    /* get recent now that pinged and locked */
  1048.   while (i <= stream->nmsgs) {    /* for each message */
  1049.                 /* number of bytes to smash or preserve */
  1050.     k = ((elt = mtx_elt (stream,i))->data2 >> 24) + mtx_size (stream,i);
  1051.     if (elt->deleted) {        /* if deleted */
  1052.       if (elt->recent) --recent;/* if recent, note one less recent message */
  1053.       delta += k;        /* number of bytes to delete */
  1054.       mail_expunged (stream,i);    /* notify upper levels */
  1055.       n++;            /* count up one more expunged message */
  1056.     }
  1057.     else if (i++ && delta) {    /* preserved message */
  1058.       j = elt->data1;        /* first byte to preserve */
  1059.       do {            /* read from source position */
  1060.     m = min (k,LOCAL->buflen);
  1061.     lseek (LOCAL->fd,j,L_SET);
  1062.     read (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1063.     pos = j - delta;    /* write to destination position */
  1064.     lseek (LOCAL->fd,pos,L_SET);
  1065.     write (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1066.     pos += m;        /* new position */
  1067.     j += m;            /* next chunk, perhaps */
  1068.       } while (k -= m);        /* until done */
  1069.       elt->data1 -= delta;    /* note the new address of this text */
  1070.     }
  1071.     else pos = elt->data1 + k;    /* preserved but no deleted messages */
  1072.   }
  1073.   if (n) {            /* truncate file after last message */
  1074.     if (pos != (LOCAL->filesize -= delta)) {
  1075.       sprintf (LOCAL->buf,"Calculated size mismatch %ld != %ld, delta = %ld",
  1076.            pos,LOCAL->filesize,delta);
  1077.       mm_log (LOCAL->buf,WARN);
  1078.       LOCAL->filesize = pos;    /* fix it then */
  1079.     }
  1080.     ftruncate (LOCAL->fd,LOCAL->filesize);
  1081.     sprintf (LOCAL->buf,"Expunged %ld messages",n);
  1082.                 /* output the news */
  1083.     mm_log (LOCAL->buf,(long) NIL);
  1084.   }
  1085.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  1086.   fsync (LOCAL->fd);        /* force disk update */
  1087.   fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1088.   LOCAL->filetime = sbuf.st_mtime;
  1089.   mm_nocritical (stream);    /* release critical */
  1090.                 /* notify upper level of new mailbox size */
  1091.   mail_exists (stream,stream->nmsgs);
  1092.   mail_recent (stream,recent);
  1093.   flock (LOCAL->fd,LOCK_SH);    /* allow sharers again */
  1094.   mtx_unlock (ld,lock);    /* release exclusive parse/append permission */
  1095. }
  1096.  
  1097. /* Mtx mail copy message(s)
  1098.     s;
  1099.  * Accepts: MAIL stream
  1100.  *        sequence
  1101.  *        destination mailbox
  1102.  * Returns: T if success, NIL if failed
  1103.  */
  1104.  
  1105. long mtx_copy (stream,sequence,mailbox)
  1106.     MAILSTREAM *stream;
  1107.     char *sequence;
  1108.     char *mailbox;
  1109. {
  1110.                 /* copy the messages */
  1111.   return (mail_sequence (stream,sequence)) ?
  1112.     mtx_copy_messages (stream,mailbox) : NIL;
  1113. }
  1114.  
  1115.  
  1116. /* Mtx mail move message(s)
  1117.     s;
  1118.  * Accepts: MAIL stream
  1119.  *        sequence
  1120.  *        destination mailbox
  1121.  * Returns: T if success, NIL if failed
  1122.  */
  1123.  
  1124. long mtx_move (stream,sequence,mailbox)
  1125.     MAILSTREAM *stream;
  1126.     char *sequence;
  1127.     char *mailbox;
  1128. {
  1129.   long i;
  1130.   struct stat sbuf;
  1131.   MESSAGECACHE *elt;
  1132.   if (!(mail_sequence (stream,sequence) &&
  1133.     mtx_copy_messages (stream,mailbox))) return NIL;
  1134.                 /* delete all requested messages */
  1135.   for (i = 1; i <= stream->nmsgs; i++)
  1136.     if ((elt = mtx_elt (stream,i))->sequence) {
  1137.       elt->deleted = T;        /* mark message deleted */
  1138.                 /* recalculate status */
  1139.       mtx_update_status (stream,i,NIL);
  1140.     }
  1141.   if (!stream->rdonly) {    /* make sure the update takes */
  1142.     fsync (LOCAL->fd);
  1143.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  1144.     LOCAL->filetime = sbuf.st_mtime;
  1145.   }
  1146.   return LONGT;
  1147. }
  1148.  
  1149. /* Mtx mail append message from stringstruct
  1150.  * Accepts: MAIL stream
  1151.  *        destination mailbox
  1152.  *        stringstruct of messages to append
  1153.  * Returns: T if append successful, else NIL
  1154.  */
  1155.  
  1156. long mtx_append (stream,mailbox,flags,date,message)
  1157.     MAILSTREAM *stream;
  1158.     char *mailbox;
  1159.     char *flags;
  1160.     char *date;
  1161.               STRING *message;
  1162. {
  1163.   struct stat sbuf;
  1164.   int fd,ld;
  1165.   char *s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  1166.   time_t tp[2];
  1167.   MESSAGECACHE elt;
  1168.   long i;
  1169.   long size = SIZE (message);
  1170.   long ret = LONGT;
  1171.   unsigned long uf = 0;
  1172.   short f = 0;
  1173.   if (flags) {            /* get flags if given */
  1174.     unsigned long ruf;
  1175.     f = mtx_getflags (user_flags (&mtxproto),flags,&ruf);
  1176.                 /* reverse bits (dontcha wish we had CIRC?) */
  1177.     while (ruf) uf |= 1 << (29 - find_rightmost_bit (&ruf));
  1178.   }
  1179.   if (date) {            /* want to preserve date? */
  1180.                 /* yes, parse date into an elt */
  1181.     if (!mail_parse_date (&elt,date)) {
  1182.       sprintf (tmp,"Bad date in append: %s",date);
  1183.       mm_log (tmp,ERROR);
  1184.       return NIL;
  1185.     }
  1186.   }
  1187.                 /* N.B.: can't use LOCAL->buf for tmp */
  1188.                 /* make sure valid mailbox */
  1189.   if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
  1190.   case ENOENT:            /* no such file? */
  1191.     mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  1192.     return NIL;
  1193.   case 0:            /* merely empty file? */
  1194.     break;
  1195.   case EINVAL:
  1196.     sprintf (tmp,"Invalid MTX-format mailbox name: %s",mailbox);
  1197.     mm_log (tmp,ERROR);
  1198.     return NIL;
  1199.   default:
  1200.     sprintf (tmp,"Not a MTX-format mailbox: %s",mailbox);
  1201.     mm_log (tmp,ERROR);
  1202.     return NIL;
  1203.   }
  1204.   if ((fd = open (mtx_file (file,mailbox),O_RDWR|O_CREAT,
  1205.           S_IREAD|S_IWRITE)) < 0) {
  1206.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1207.     mm_log (tmp,ERROR);
  1208.     return NIL;
  1209.   }
  1210.                 /* get exclusive parse/append permission */
  1211.   if ((ld = mtx_lock (fd,lock,LOCK_EX)) < 0) {
  1212.     mm_log ("Unable to lock append mailbox",ERROR);
  1213.     return NIL;
  1214.   }
  1215.   s = (char *) fs_get (size);    /* copy message */
  1216.   for (i = 0; i < size; s[i++] = SNX (message));
  1217.  
  1218.   mm_critical (stream);        /* go critical */
  1219.   fstat (fd,&sbuf);        /* get current file size */
  1220.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1221.   if (date) mail_date(tmp,&elt);/* write preseved date */
  1222.   else internal_date (tmp);    /* get current date in IMAP format */
  1223.                 /* add remainder of header */
  1224.   sprintf (tmp+26,",%ld;%010lo%02o\015\012",size,uf,f);
  1225.                 /* write header */
  1226.   if ((write (fd,tmp,strlen (tmp)) < 0) || ((write (fd,s,size)) < 0) ||
  1227.       fsync (fd)) {
  1228.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1229.     mm_log (tmp,ERROR);
  1230.     ftruncate (fd,sbuf.st_size);
  1231.     ret = NIL;
  1232.   }
  1233.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1234.   tp[1] = sbuf.st_mtime;
  1235.   utime (file,tp);        /* set the times */
  1236.   mtx_unlock (ld,lock);        /* release exclusive parse/append permission */
  1237.   close (fd);            /* close the file */
  1238.   mm_nocritical (stream);    /* release critical */
  1239.   fs_give ((void **) &s);    /* flush the buffer */
  1240.   return ret;
  1241. }
  1242.  
  1243. /* Mtx garbage collect stream
  1244.  * Accepts: Mail stream
  1245.  *        garbage collection flags
  1246.  */
  1247.  
  1248. void mtx_gc (stream,gcflags)
  1249.     MAILSTREAM *stream;
  1250.     long gcflags;
  1251. {
  1252.   /* nothing here for now */
  1253. }
  1254.  
  1255. /* Internal routines */
  1256.  
  1257.  
  1258. /* Mtx mail lock file for parse/append permission
  1259.  * Accepts: file descriptor
  1260.  *        lock file name buffer
  1261.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1262.  * Returns: file descriptor of lock or -1 if failure
  1263.  */
  1264.  
  1265. int mtx_lock (fd,lock,op)
  1266.     int fd;
  1267.     char *lock;
  1268.     int op;
  1269. {
  1270.   int ld;
  1271.   struct stat sbuf;
  1272.   if (!fstat (fd,&sbuf)) {    /* get data for this file */
  1273.                 /* make temporary file name */
  1274.     sprintf (lock,"/tmp/.%hx.%lx",sbuf.st_dev,sbuf.st_ino);
  1275.     if (chk_notsymlink (lock,T) &&
  1276.     ((ld = open (lock,O_RDWR|O_CREAT,
  1277.              (int)mail_parameters(NIL,GET_LOCKPROTECTION,NIL))) >= 0)){
  1278.       flock (ld,op);        /* get this lock */
  1279.       return ld;        /* return locking file descriptor */
  1280.     }
  1281.   }
  1282.   return -1;
  1283. }
  1284.  
  1285.  
  1286. /* Mtx mail unlock file for parse/append permission
  1287.  * Accepts: file descriptor
  1288.  *        lock file name from mtx_lock()
  1289.  */
  1290.  
  1291. void mtx_unlock (fd,lock)
  1292.     int fd;
  1293.     char *lock;
  1294. {
  1295.   unlink (lock);        /* delete the file */
  1296.   flock (fd,LOCK_UN);        /* unlock it */
  1297.   close (fd);            /* close it */
  1298. }
  1299.  
  1300. /* Mtx mail return internal message size in bytes
  1301.  * Accepts: MAIL stream
  1302.  *        message #
  1303.  * Returns: internal size of message
  1304.  */
  1305.  
  1306. unsigned long mtx_size (stream,m)
  1307.     MAILSTREAM *stream;
  1308.     long m;
  1309. {
  1310.   MESSAGECACHE *elt = mail_elt (stream,m);
  1311.   return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->data1 : LOCAL->filesize)
  1312.     - (elt->data1 + (elt->data2 >> 24));
  1313. }
  1314.  
  1315.  
  1316. /* Mtx mail generate file string
  1317.  * Accepts: temporary buffer to write into
  1318.  *        mailbox name string
  1319.  * Returns: local file string or NIL if failure
  1320.  */
  1321.  
  1322. char *mtx_file (dst,name)
  1323.     char *dst;
  1324.     char *name;
  1325. {
  1326.   char tmp[MAILTMPLEN];
  1327.   char *s = mailboxfile (dst,name);
  1328.                 /* return our standard inbox */
  1329.   return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX.MTX",tmp) ?
  1330.                    "INBOX.MTX" : "mail.txt") : s;
  1331. }
  1332.  
  1333. /* Parse flag list
  1334.  * Accepts: MAIL stream
  1335.  *        flag list as a character string
  1336.  *        pointer to user flags to return
  1337.  * Returns: system flags
  1338.  */
  1339.  
  1340. long mtx_getflags (stream,flag,uf)
  1341.     MAILSTREAM *stream;
  1342.     char *flag;
  1343.     unsigned long *uf;
  1344. {
  1345.   char key[MAILTMPLEN];
  1346.   char *t,*s,tmp[MAILTMPLEN];
  1347.   short f = 0;
  1348.   long i;
  1349.   short j;
  1350.   *uf = 0;            /* initially no user flags */
  1351.   if (flag && *flag) {        /* no-op if no flag string */
  1352.                 /* check if a list and make sure valid */
  1353.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1354.       mm_log ("Bad flag list",ERROR);
  1355.       return NIL;
  1356.     }
  1357.                 /* copy the flag string w/o list construct */
  1358.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1359.     tmp[j] = '\0';        /* tie off tail */
  1360.                 /* make uppercase, find first, parse */
  1361.     if (t = strtok (ucase (tmp)," ")) do {
  1362.       i = 0;            /* no flag yet */
  1363.                 /* system flag, dispatch on first character */
  1364.       if (*t == '\\') switch (*++t) {
  1365.       case 'S':            /* possible \Seen flag */
  1366.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1367.       f |= i = fSEEN;
  1368.     break;
  1369.       case 'D':            /* possible \Deleted flag */
  1370.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1371.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1372.     break;
  1373.       case 'F':            /* possible \Flagged flag */
  1374.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1375.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1376.     break;
  1377.       case 'A':            /* possible \Answered flag */
  1378.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1379.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1380.       f |= i = fANSWERED;
  1381.     break;
  1382.       default:            /* unknown */
  1383.     break;
  1384.       }
  1385.  
  1386.                 /* user flag, search through table */
  1387.       else for (j = 0; !i && j < NUSERFLAGS && (s =stream->user_flags[j]); ++j)
  1388.     if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
  1389.       if (!i) {            /* didn't find a matching flag? */
  1390.     sprintf (key,"Unknown flag: %.80s",t);
  1391.     mm_log (key,ERROR);
  1392.       }
  1393.                 /* parse next flag */
  1394.     } while (t = strtok (NIL," "));
  1395.   }
  1396.   return f;
  1397. }
  1398.  
  1399. /* Mtx mail parse mailbox
  1400.  * Accepts: MAIL stream
  1401.  * Returns: T if parse OK
  1402.  *        NIL if failure, stream aborted
  1403.  */
  1404.  
  1405. long mtx_parse (stream)
  1406.     MAILSTREAM *stream;
  1407. {
  1408.   struct stat sbuf;
  1409.   MESSAGECACHE *elt = NIL;
  1410.   char c,*s,*t,*x;
  1411.   char tmp[MAILTMPLEN];
  1412.   unsigned long i,j,msiz;
  1413.   long curpos = LOCAL->filesize;
  1414.   long nmsgs = stream->nmsgs;
  1415.   long recent = stream->recent;
  1416.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1417.   if (sbuf.st_size < curpos) {    /* sanity check */
  1418.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  1419.     mm_log (tmp,ERROR);
  1420.     mtx_close (stream);
  1421.     return NIL;
  1422.   }
  1423.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  1424.                 /* get to that position in the file */
  1425.     lseek (LOCAL->fd,curpos,L_SET);
  1426.     if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
  1427.       sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
  1428.            curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
  1429.       mm_log (tmp,ERROR);
  1430.       mtx_close (stream);
  1431.       return NIL;
  1432.     }
  1433.     LOCAL->buf[i] = '\0';    /* tie off buffer just in case */
  1434.     if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
  1435.       sprintf (tmp,"Unable to find CRLF at %ld in %ld bytes, text: %s",
  1436.            curpos,i,LOCAL->buf);
  1437.       mm_log (tmp,ERROR);
  1438.       mtx_close (stream);
  1439.       return NIL;
  1440.     }
  1441.     *s = '\0';            /* tie off header line */
  1442.     i = (s + 2) - LOCAL->buf;    /* note start of text offset */
  1443.     if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
  1444.       sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,
  1445.            LOCAL->buf);
  1446.       mm_log (tmp,ERROR);
  1447.       mtx_close (stream);
  1448.       return NIL;
  1449.     }
  1450.  
  1451.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1452.                 /* intantiate an elt for this message */
  1453.     (elt = mail_elt (stream,++nmsgs))->valid = T;
  1454.     elt->data1 = curpos;    /* note file offset of header */
  1455.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1456.                 /* parse the header components */
  1457.     if (!(mail_parse_date (elt,LOCAL->buf) &&
  1458.       (elt->rfc822_size = msiz = strtol (x = s,&s,10)) && (!(s && *s)) &&
  1459.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1460.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1461.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1462.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1463.       sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
  1464.            curpos,LOCAL->buf,x,t);
  1465.       mm_log (tmp,ERROR);
  1466.       mtx_close (stream);
  1467.       return NIL;
  1468.     }
  1469.                 /* make sure message fits in file */
  1470.     if ((curpos += (msiz + i)) > sbuf.st_size) {
  1471.       mm_log ("Last message runs past end of file",ERROR);
  1472.       mtx_close (stream);
  1473.       return NIL;
  1474.     }
  1475.     c = t[10];            /* remember first system flags byte */
  1476.     t[10] = '\0';        /* tie off flags */
  1477.     j = strtol (t,NIL,8);    /* get user flags value */
  1478.     t[10] = c;            /* restore first system flags byte */
  1479.                 /* set up all valid user flags (reversed!) */
  1480.     while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1481.           stream->user_flags[i]) elt->user_flags |= 1 << i;
  1482.                 /* calculate system flags */
  1483.     if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1484.     if (j & fDELETED) elt->deleted = T;
  1485.     if (j & fFLAGGED) elt->flagged = T;
  1486.     if (j & fANSWERED) elt->answered = T;
  1487.     if (!(j & fOLD)) {        /* newly arrived message? */
  1488.       elt->recent = T;
  1489.       recent++;            /* count up a new recent message */
  1490.                 /* mark it as old */
  1491.       mtx_update_status (stream,nmsgs,NIL);
  1492.     }
  1493.   }
  1494.   fsync (LOCAL->fd);        /* make sure all the fOLD flags take */
  1495.                 /* update parsed file size and time */
  1496.   LOCAL->filesize = sbuf.st_size;
  1497.   fstat (LOCAL->fd,&sbuf);    /* get status again to ensure time is right */
  1498.   LOCAL->filetime = sbuf.st_mtime;
  1499.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1500.   mail_recent (stream,recent);    /* and of change in recent messages */
  1501.   return LONGT;            /* return the winnage */
  1502. }
  1503.  
  1504. /* Mtx copy messages
  1505.  * Accepts: MAIL stream
  1506.  *        mailbox copy vector
  1507.  *        mailbox name
  1508.  * Returns: T if success, NIL if failed
  1509.  */
  1510.  
  1511. long mtx_copy_messages (stream,mailbox)
  1512.     MAILSTREAM *stream;
  1513.     char *mailbox;
  1514. {
  1515.   struct stat sbuf;
  1516.   time_t tp[2];
  1517.   MESSAGECACHE *elt;
  1518.   unsigned long i,j,k;
  1519.   long ret = LONGT;
  1520.   int fd,ld;
  1521.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  1522.                 /* make sure valid mailbox */
  1523.   if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
  1524.   case ENOENT:            /* no such file? */
  1525.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  1526.     return NIL;
  1527.   case 0:            /* merely empty file? */
  1528.     break;
  1529.   case EINVAL:
  1530.     sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %s",mailbox);
  1531.     mm_log (LOCAL->buf,ERROR);
  1532.     return NIL;
  1533.   default:
  1534.     sprintf (LOCAL->buf,"Not a MTX-format mailbox: %s",mailbox);
  1535.     mm_log (LOCAL->buf,ERROR);
  1536.     return NIL;
  1537.   }
  1538.                 /* got file? */
  1539.   if ((fd = open (mtx_file (file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0){
  1540.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  1541.     mm_log (LOCAL->buf,ERROR);
  1542.     return NIL;
  1543.   }
  1544.   mm_critical (stream);        /* go critical */
  1545.                 /* get exclusive parse/append permission */
  1546.   if ((ld = mtx_lock (fd,lock,LOCK_EX)) < 0) {
  1547.     mm_log ("Unable to lock copy mailbox",ERROR);
  1548.     return NIL;
  1549.   }
  1550.   fstat (fd,&sbuf);        /* get current file size */
  1551.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1552.  
  1553.                 /* for each requested message */
  1554.   for (i = 1; ret && (i <= stream->nmsgs); i++)
  1555.     if ((elt = mail_elt (stream,i))->sequence) {
  1556.       lseek (LOCAL->fd,elt->data1,L_SET);
  1557.                 /* number of bytes to copy */
  1558.       k = (elt->data2 >> 24) + mtx_size (stream,i);
  1559.       do {            /* read from source position */
  1560.     j = min (k,LOCAL->buflen);
  1561.     read (LOCAL->fd,LOCAL->buf,(unsigned int) j);
  1562.     if ((write (fd,LOCAL->buf,(unsigned int) j) < 0) || fsync (fd)) {
  1563.       sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  1564.       mm_log (LOCAL->buf,ERROR);
  1565.       ftruncate (fd,sbuf.st_size);
  1566.       j = k;
  1567.       ret = NIL;        /* note error */
  1568.       break;
  1569.     }
  1570.       } while (k -= j);        /* until done */
  1571.     }
  1572.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1573.   tp[1] = sbuf.st_mtime;
  1574.   utime (file,tp);        /* set the times */
  1575.   mtx_unlock (ld,lock);        /* release exclusive parse/append permission */
  1576.   close (fd);            /* close the file */
  1577.   mm_nocritical (stream);    /* release critical */
  1578.   return ret;
  1579. }
  1580.  
  1581. /* Mtx get cache element with status updating from file
  1582.  * Accepts: MAIL stream
  1583.  *        message number
  1584.  * Returns: cache element
  1585.  */
  1586.  
  1587. MESSAGECACHE *mtx_elt (stream,msgno)
  1588.     MAILSTREAM *stream;
  1589.     long msgno;
  1590. {
  1591.   unsigned long i,j,sysflags;
  1592.   char c;
  1593.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1594.   unsigned long oldsysflags = (elt->seen ? fSEEN : NIL) |
  1595.     (elt->deleted ? fDELETED : NIL) | (elt->flagged ? fFLAGGED : NIL) |
  1596.       (elt->answered ? fANSWERED : NIL);
  1597.   unsigned long olduserflags = elt->user_flags;
  1598.                 /* set the seek pointer */
  1599.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 14,L_SET);
  1600.                 /* read the new flags */
  1601.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1602.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1603.     fatal (LOCAL->buf);
  1604.   }
  1605.                 /* calculate system flags */
  1606.   sysflags = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0') &
  1607.     (fSEEN|fDELETED|fFLAGGED|fANSWERED);
  1608.   elt->seen = sysflags & fSEEN ? T : NIL;
  1609.   elt->deleted = sysflags & fDELETED ? T : NIL;
  1610.   elt->flagged = sysflags & fFLAGGED ? T : NIL;
  1611.   elt->answered = sysflags & fANSWERED ? T : NIL;
  1612.   c = LOCAL->buf[10];        /* remember first system flags byte */
  1613.   LOCAL->buf[10] = '\0';    /* tie off flags */
  1614.   j = strtol (LOCAL->buf,NIL,8);/* get user flags value */
  1615.   LOCAL->buf[10] = c;        /* restore first system flags byte */
  1616.                 /* set up all valid user flags (reversed!) */
  1617.   while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1618.         stream->user_flags[i]) elt->user_flags |= 1 << i;
  1619.   if ((oldsysflags != sysflags) || (olduserflags != elt->user_flags))
  1620.     mm_flags (stream,msgno);    /* let top level know */
  1621.   return elt;
  1622. }
  1623.  
  1624. /* Mtx update status string
  1625.  * Accepts: MAIL stream
  1626.  *        message number
  1627.  *        flag saying whether or not to sync
  1628.  */
  1629.  
  1630. void mtx_update_status (stream,msgno,syncflag)
  1631.     MAILSTREAM *stream;
  1632.     long msgno;
  1633.     long syncflag;
  1634. {
  1635.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1636.   struct stat sbuf;
  1637.   unsigned long j,k = 0;
  1638.   if (!stream->rdonly) {    /* not if readonly you don't */
  1639.     j = elt->user_flags;    /* get user flags */
  1640.                 /* reverse bits (dontcha wish we had CIRC?) */
  1641.     while (j) k |= 1 << (29 - find_rightmost_bit (&j));
  1642.                 /* print new flag string */
  1643.     sprintf (LOCAL->buf,"%010lo%02o",k,
  1644.          fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1645.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1646.                 /* get to that place in the file */
  1647.     lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 14,L_SET);
  1648.                 /* write new flags */
  1649.     write (LOCAL->fd,LOCAL->buf,12);
  1650.                 /* sync if requested */
  1651.     if (syncflag) fsync (LOCAL->fd);
  1652.     fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1653.     LOCAL->filetime = sbuf.st_mtime;
  1654.   }
  1655. }
  1656.  
  1657. /* Search support routines
  1658.  * Accepts: MAIL stream
  1659.  *        message number
  1660.  *        pointer to additional data
  1661.  * Returns: T if search matches, else NIL
  1662.  */
  1663.  
  1664.  
  1665. char mtx_search_all (stream,msgno,d,n)
  1666.     MAILSTREAM *stream;
  1667.     long msgno;
  1668.     char *d;
  1669.     long n;
  1670. {
  1671.   return T;            /* ALL always succeeds */
  1672. }
  1673.  
  1674.  
  1675. char mtx_search_answered (stream,msgno,d,n)
  1676.     MAILSTREAM *stream;
  1677.     long msgno;
  1678.     char *d;
  1679.     long n;
  1680. {
  1681.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1682. }
  1683.  
  1684.  
  1685. char mtx_search_deleted (stream,msgno,d,n)
  1686.     MAILSTREAM *stream;
  1687.     long msgno;
  1688.     char *d;
  1689.     long n;
  1690. {
  1691.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1692. }
  1693.  
  1694.  
  1695. char mtx_search_flagged (stream,msgno,d,n)
  1696.     MAILSTREAM *stream;
  1697.     long msgno;
  1698.     char *d;
  1699.     long n;
  1700. {
  1701.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1702. }
  1703.  
  1704.  
  1705. char mtx_search_keyword (stream,msgno,d,n)
  1706.     MAILSTREAM *stream;
  1707.     long msgno;
  1708.     char *d;
  1709.     long n;
  1710. {
  1711.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1712. }
  1713.  
  1714.  
  1715. char mtx_search_new (stream,msgno,d,n)
  1716.     MAILSTREAM *stream;
  1717.     long msgno;
  1718.     char *d;
  1719.     long n;
  1720. {
  1721.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1722.   return (elt->recent && !elt->seen) ? T : NIL;
  1723. }
  1724.  
  1725. char mtx_search_old (stream,msgno,d,n)
  1726.     MAILSTREAM *stream;
  1727.     long msgno;
  1728.     char *d;
  1729.     long n;
  1730. {
  1731.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1732. }
  1733.  
  1734.  
  1735. char mtx_search_recent (stream,msgno,d,n)
  1736.     MAILSTREAM *stream;
  1737.     long msgno;
  1738.     char *d;
  1739.     long n;
  1740. {
  1741.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1742. }
  1743.  
  1744.  
  1745. char mtx_search_seen (stream,msgno,d,n)
  1746.     MAILSTREAM *stream;
  1747.     long msgno;
  1748.     char *d;
  1749.     long n;
  1750. {
  1751.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1752. }
  1753.  
  1754.  
  1755. char mtx_search_unanswered (stream,msgno,d,n)
  1756.     MAILSTREAM *stream;
  1757.     long msgno;
  1758.     char *d;
  1759.     long n;
  1760. {
  1761.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1762. }
  1763.  
  1764.  
  1765. char mtx_search_undeleted (stream,msgno,d,n)
  1766.     MAILSTREAM *stream;
  1767.     long msgno;
  1768.     char *d;
  1769.     long n;
  1770. {
  1771.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1772. }
  1773.  
  1774.  
  1775. char mtx_search_unflagged (stream,msgno,d,n)
  1776.     MAILSTREAM *stream;
  1777.     long msgno;
  1778.     char *d;
  1779.     long n;
  1780. {
  1781.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1782. }
  1783.  
  1784.  
  1785. char mtx_search_unkeyword (stream,msgno,d,n)
  1786.     MAILSTREAM *stream;
  1787.     long msgno;
  1788.     char *d;
  1789.     long n;
  1790. {
  1791.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1792. }
  1793.  
  1794.  
  1795. char mtx_search_unseen (stream,msgno,d,n)
  1796.     MAILSTREAM *stream;
  1797.     long msgno;
  1798.     char *d;
  1799.     long n;
  1800. {
  1801.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1802. }
  1803.  
  1804. char mtx_search_before (stream,msgno,d,n)
  1805.     MAILSTREAM *stream;
  1806.     long msgno;
  1807.     char *d;
  1808.     long n;
  1809. {
  1810.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1811.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1812. }
  1813.  
  1814.  
  1815. char mtx_search_on (stream,msgno,d,n)
  1816.     MAILSTREAM *stream;
  1817.     long msgno;
  1818.     char *d;
  1819.     long n;
  1820. {
  1821.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1822.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1823. }
  1824.  
  1825.  
  1826. char mtx_search_since (stream,msgno,d,n)
  1827.     MAILSTREAM *stream;
  1828.     long msgno;
  1829.     char *d;
  1830.     long n;
  1831. {
  1832.                 /* everybody interprets "since" as .GE. */
  1833.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1834.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1835. }
  1836.  
  1837.  
  1838. char mtx_search_body (stream,msgno,d,n)
  1839.     MAILSTREAM *stream;
  1840.     long msgno;
  1841.     char *d;
  1842.     long n;
  1843. {
  1844.   char ret;
  1845.   unsigned long hdrsize;
  1846.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  1847.   unsigned long textsize = mtx_size (stream,msgno) - hdrsize;
  1848.   char *s = (char *) fs_get (1 + textsize);
  1849.   s[textsize] = '\0';        /* tie off string */
  1850.                 /* recalculate status */
  1851.   mtx_update_status (stream,msgno,T);
  1852.                 /* get to text position */
  1853.   lseek (LOCAL->fd,hdrpos + hdrsize,L_SET);
  1854.   read (LOCAL->fd,s,textsize);    /* slurp the data */
  1855.                 /* copy the string */
  1856.   ret = search (s,textsize,d,n);/* do the search */
  1857.   fs_give ((void **) &s);    /* flush readin buffer */
  1858.   return ret;            /* return search value */
  1859. }
  1860.  
  1861.  
  1862. char mtx_search_subject (stream,msgno,d,n)
  1863.     MAILSTREAM *stream;
  1864.     long msgno;
  1865.     char *d;
  1866.     long n;
  1867. {
  1868.   char *s = mtx_fetchstructure (stream,msgno,NIL)->subject;
  1869.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1870. }
  1871.  
  1872.  
  1873. char mtx_search_text (stream,msgno,d,n)
  1874.     MAILSTREAM *stream;
  1875.     long msgno;
  1876.     char *d;
  1877.     long n;
  1878. {
  1879.   char ret;
  1880.   unsigned long hdrsize;
  1881.   unsigned long hdrpos = mtx_header (stream,msgno,&hdrsize);
  1882.   char *s = (char *) fs_get (1 + hdrsize);
  1883.   s[hdrsize] = '\0';        /* tie off string */
  1884.                 /* get to header position */
  1885.   lseek (LOCAL->fd,hdrpos,L_SET);
  1886.   read (LOCAL->fd,s,hdrsize);    /* slurp the data */
  1887.   ret = search (s,hdrsize,d,n) || mtx_search_body (stream,msgno,d,n);
  1888.   fs_give ((void **) &s);    /* flush readin buffer */
  1889.   return ret;            /* return search value */
  1890. }
  1891.  
  1892. char mtx_search_bcc (stream,msgno,d,n)
  1893.     MAILSTREAM *stream;
  1894.     long msgno;
  1895.     char *d;
  1896.     long n;
  1897. {
  1898.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->bcc;
  1899.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1900.                 /* get text for address */
  1901.   rfc822_write_address (LOCAL->buf,a);
  1902.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1903. }
  1904.  
  1905.  
  1906. char mtx_search_cc (stream,msgno,d,n)
  1907.     MAILSTREAM *stream;
  1908.     long msgno;
  1909.     char *d;
  1910.     long n;
  1911. {
  1912.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->cc;
  1913.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1914.                 /* get text for address */
  1915.   rfc822_write_address (LOCAL->buf,a);
  1916.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1917. }
  1918.  
  1919.  
  1920. char mtx_search_from (stream,msgno,d,n)
  1921.     MAILSTREAM *stream;
  1922.     long msgno;
  1923.     char *d;
  1924.     long n;
  1925. {
  1926.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->from;
  1927.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1928.                 /* get text for address */
  1929.   rfc822_write_address (LOCAL->buf,a);
  1930.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1931. }
  1932.  
  1933.  
  1934. char mtx_search_to (stream,msgno,d,n)
  1935.     MAILSTREAM *stream;
  1936.     long msgno;
  1937.     char *d;
  1938.     long n;
  1939. {
  1940.   ADDRESS *a = mtx_fetchstructure (stream,msgno,NIL)->to;
  1941.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1942.                 /* get text for address */
  1943.   rfc822_write_address (LOCAL->buf,a);
  1944.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1945. }
  1946.  
  1947. /* Search parsers */
  1948.  
  1949.  
  1950. /* Parse a date
  1951.  * Accepts: function to return
  1952.  *        pointer to date integer to return
  1953.  * Returns: function to return
  1954.  */
  1955.  
  1956. search_t mtx_search_date (f,n)
  1957.     search_t f;
  1958.     long *n;
  1959. {
  1960.   long i;
  1961.   char *s;
  1962.   MESSAGECACHE elt;
  1963.                 /* parse the date and return fn if OK */
  1964.   return (mtx_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1965.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1966. }
  1967.  
  1968. /* Parse a flag
  1969.  * Accepts: function to return
  1970.  *        pointer to keyword integer to return
  1971.  *        MAIL stream
  1972.  * Returns: function to return
  1973.  */
  1974.  
  1975. search_t mtx_search_flag (f,n,stream)
  1976.     search_t f;
  1977.     long *n;
  1978.     MAILSTREAM *stream;
  1979. {
  1980.   short i;
  1981.   char *s,*t;
  1982.   if (t = strtok (NIL," ")) {    /* get a keyword */
  1983.     ucase (t);            /* get uppercase form of flag */
  1984.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  1985.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  1986.   }
  1987.   return NIL;            /* couldn't find keyword */
  1988. }
  1989.  
  1990. /* Parse a string
  1991.  * Accepts: function to return
  1992.  *        pointer to string to return
  1993.  *        pointer to string length to return
  1994.  * Returns: function to return
  1995.  */
  1996.  
  1997.  
  1998. search_t mtx_search_string (f,d,n)
  1999.     search_t f;
  2000.     char **d;
  2001.     long *n;
  2002. {
  2003.   char *end = " ";
  2004.   char *c = strtok (NIL,"");    /* remainder of criteria */
  2005.   if (!c) return NIL;        /* missing argument */
  2006.   switch (*c) {            /* see what the argument is */
  2007.   case '{':            /* literal string */
  2008.     *n = strtol (c+1,d,10);    /* get its length */
  2009.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  2010.     (!(*(c = *d + *n)) || (*c == ' '))) {
  2011.       char e = *--c;
  2012.       *c = DELIM;        /* make sure not a space */
  2013.       strtok (c," ");        /* reset the strtok mechanism */
  2014.       *c = e;            /* put character back */
  2015.       break;
  2016.     }
  2017.   case '\0':            /* catch bogons */
  2018.   case ' ':
  2019.     return NIL;
  2020.   case '"':            /* quoted string */
  2021.     if (strchr (c+1,'"')) end = "\"";
  2022.     else return NIL;
  2023.   default:            /* atomic string */
  2024.     if (*d = strtok (c,end)) *n = strlen (*d);
  2025.     else return NIL;
  2026.     break;
  2027.   }
  2028.   return f;
  2029. }
  2030.